[csapp] Lab5 Shell Lab

Lab5 Shell Lab

Introduction

In this lab, we need to implement a simple Unix style shell program. The file we only need to modify is tsh.c in handout package.

We should implement those functions:

  1. eval parse and execute the instruction that user has inputted.
  2. builtin_cmd check if the command inputted is a inner build command. Such as quit fg bg
  3. do_bgfg It define the behaviors how to execute back ground and front ground command.
  4. waitfg wait until the front ground program done.
  5. sigchld_handler process SIGCHLD signal, when son process stopped or terminated.
  6. sigint_handler process SIGINT signal, interrupt from keyboard ctrl-c.
  7. sigstp_handler process SIGTSTP signal, interrupt from keyboard ctrl-z.

You can use make test(n) to test your shell program on n-th test set. For example, if you have implemented all functions this lab required. You can use make test15 to test your tsh.

Methods

1. eval

Just borrowed the code from csapp 🙂.

A quick review of signal process function. If you want to use those functions, you need include signal.h first.

  • int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) Use how to tell function, which way we want to change the blocked signal set. If how = SIG_BLOCK, blocked = blocked | set if how = SIG_UNBLOCK, blocked = blocked & ~set if how = SETMASK, block = set if oldset is not empty, the previous blocked status will be stored in oldset, for recovery purpose.
  • int sigemptyset(sigset_t *set) Init set to 0.
  • int sigfillset(sigset_t *set) Get current signals.
  • int sigaddset(sigset_t *set, int signum) Add signum to signal set.
  • int sigdelset(sigset_t *set, int signum) Delete signum from signal set.
  • int sigismember(const sigset_t *set, int signum) If signum in signal set, return 1 else return 0.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void eval(char *cmdline) 
{
char *argv[MAXARGS];
char buf[MAXLINE];
int bg;
int state;
pid_t pid;
sigset_t mask_all, mask_one, prev;

strcpy(buf, cmdline);
bg = parseline(buf, argv); // this function is exists in this lab's code

if (argv[0] == NULL) // is there are no command to process. just return.
return ;

if(!builtin_cmd(argv))
{
sigfillset(&mask_all); // get current mask
sigemptyset(&mask_one); // init, set mask_one to 0
sigaddset(&mask_one, SIGCHLD); // Add SIGCHLD signal to mask_one set.
sigprocmask(SIG_BLOCK, &mask_one, &prev); // blocked = bloked | mask_one
if ((pid = fork()) == 0)
{
sigprocmask(SIG_SETMASK, &prev, NULL);
if (setpgid(0, 0) < 0)
{
perror("SETPGID ERROR");
exit(0);
}
if (execve(argv[0], argv, environ) < 0)
{
printf("%s: Command not found\n", argv[0]);
exit(0);
}
}
else
{
state = bg ? BG : FG;
sigprocmask(SIG_BLOCK, &mask_all, NULL);
addjob(jobs, pid, state, cmdline);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
if (!bg)
waitfg(pid);
else
printf("[%d] (%d) %s",pid2jid(pid), pid, cmdline);
}
return;
}

2. builtin_cmd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int builtin_cmd(char **argv) 
{
if (!strcmp(argv[0], "quit"))
exit(0);
else if (!strcmp(argv[0], "jobs"))
{
listjobs(jobs);
return 1;
}
else if (!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg"))
{
do_bgfg(argv);
return 1;
}

else if (!strcmp(argv[0], "&"))
{
return 1;
}
return 0; /* not a builtin command */
}

3. do_bgfg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void do_bgfg(char **argv) 
{
if (argv[1] == NULL)
{
printf("%s command requires PID or %%jobid argument\n", argv[0]);
return;
}

struct job_t* job;
int id;

if (sscanf(argv[1], "%%%d", &id) > 0)
{
job = getjobjid(jobs, id);
if (job == NULL)
{
printf("%%%d: No such job\n", id);
return ;
}
}

else if (sscanf(argv[1], "%d", &id) > 0)
{
job = getjobpid(jobs, id);
if (job == NULL)
{
printf("(%d): No such process\n", id);
return ;
}
}

else
{
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}

if(!strcmp(argv[0], "bg"))
{
kill(-(job->pid), SIGCONT);
job->state = BG;
printf("[%d] (%d) %s",job->jid, job->pid, job->cmdline);
}
else
{
kill(-(job->pid), SIGCONT);
job->state = FG;
waitfg(job->pid);
}
return;
}

4. waitfg

1
2
3
4
sigset_t mask_temp;
sigemptyset(&mask_temp);
while (fgpid(jobs) > 0) sigsuspend(&mask_temp);
return;

5. sigchld_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void sigchld_handler(int sig) 
{
int olderrno = errno;
pid_t pid;
int status;
sigset_t mask_all, prev;

sigfillset(&mask_all);
while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
{
if (WIFEXITED(status)){
sigprocmask(SIG_BLOCK, &mask_all, &prev);
deletejob(jobs, pid);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
else if (WIFSIGNALED(status))
{
struct job_t* job = getjobpid(jobs, pid);
sigprocmask(SIG_BLOCK, &mask_all, &prev);
printf("Job [%d] (%d) terminated by signal %d\n", job->jid, job->pid, WTERMSIG(status));
deletejob(jobs, pid);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
else
{
struct job_t* job = getjobpid(jobs, pid);
sigprocmask(SIG_BLOCK, &mask_all, &prev);
printf("Job [%d] (%d) stopped by signal %d\n", job->jid, job->pid, WSTOPSIG(status));
job->state= ST;
sigprocmask(SIG_SETMASK, &prev, NULL);
}
}
errno = olderrno;
return;
}

6. sigint_handler

1
2
3
4
5
6
7
8
9
10
void sigint_handler(int sig) 
{
int olderrno = errno;
pid_t pid = fgpid(jobs);
if (pid != 0)
kill(-pid, sig);
errno = olderrno;

return;
}

If ctrl-c is pressed, you should kill all front ground process.

7. sigtstp_handler

1
2
3
4
5
6
7
8
9
void sigtstp_handler(int sig) 
{
int olderrno = errno;
pid_t pid = fgpid(jobs);
if (pid != 0)
kill(-pid, sig);
errno = olderrno;
return;
}

If ctrl-z is pressed, you should kill all front ground process.

作者

WangCH

发布于

2022-03-17

更新于

2022-03-17

许可协议

评论